/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.metadata.service.dev.core.entity;

import com.fasterxml.jackson.databind.JsonNode;
import com.inspur.edp.lcm.metadata.api.IMetadataContent;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.core.persistence.RepositoryFactory;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

/**
 * 元数据工程下元数据缓存结构
 *
 * @author sunhongfei
 */
public class GspMetadataCache {
    // 元数据文件反序列化元数据结构,不携带content属性
    @Getter
    private final GspMetadata metadataWithoutContent;
    // 元数据文件的上次修改时间,用于在文件发生变更后更新缓存
    @Getter
    private final long cacheModifyTime;
    // 元数据文件后缀,标识元数据类型,用于快速在缓存中过滤出指定类型元数据
    @Getter
    private final String fileExtension;
    @Getter
    private final String filePath;
    @Getter
    private final String metadataId;
    // 元数据文件中的content部分json节点
    private JsonNode contentJson;
    // 元数据文件反序列化元数据结构,携带content属性
    private GspMetadata metadataWithContent;

    /**
     * 根据元数据文件获取元数据文件信息,读取内容json结构并将其中除content节点外反序列化为元数据结构,记录为缓存
     *
     * @param metadataWithoutContent 元数据文件除content节点外反序列化的元数据结构
     * @param contentJson            元数据文件内容中content节点json结构
     * @param cacheModifyTime        元数据文件上次修改时间
     * @param fileExtension          元数据文件后缀,对应元数据类型,便于在缓存中过滤指定类型的元数据
     */
    public GspMetadataCache(GspMetadata metadataWithoutContent,
                            JsonNode contentJson, long cacheModifyTime,
                            String fileExtension, String filePath) {
        this.metadataWithoutContent = metadataWithoutContent;
        Assert.notNull(metadataWithoutContent, "元数据信息不能为空");
        this.contentJson = contentJson;
        Assert.notNull(contentJson, "元数据内容不能为空");
        this.cacheModifyTime = cacheModifyTime;
        this.filePath = filePath;
        Assert.notNull(filePath, "元数据文件路径不能为空");
        this.fileExtension = StringUtils.lowerCase(fileExtension);
        this.metadataId = metadataWithoutContent.getHeader().getId();
    }

    /**
     * 根据缓存获取元数据文件对应的元数据结构(携带content内容)
     *
     * @return 元数据结构(携带content内容)
     */
    public GspMetadata getMetadataWithContent() {
        // 若缓存中已存在,则直接返回缓存中内容
        if (metadataWithContent != null) {
            return metadataWithContent;
        }
        // 首先浅拷贝一份 无content元数据结构, 以防止影响其他场景使用此结构时产生混淆
        GspMetadata metadata = cloneMetadataWithoutContent(metadataWithoutContent);
        // 反序列化缓存中携带的 content json结构, 赋值到元数据结构中
        IMetadataContent content = RepositoryFactory.getInstance().getMetadataRepository().deserializeMetadataContent(metadata.getHeader().getType(), contentJson);
        metadata.setContent(content);
        this.metadataWithContent = metadata;
        // content已被序列化,其json格式已无存在必要,将其释放
        this.contentJson = null;
        return this.metadataWithContent;
    }

    private GspMetadata cloneMetadataWithoutContent(GspMetadata sourceMetadata) {
        GspMetadata targetMetadata = new GspMetadata();
        targetMetadata.setHeader(sourceMetadata.getHeader());
        targetMetadata.setRelativePath(sourceMetadata.getRelativePath());
        targetMetadata.setExtendProperty(sourceMetadata.getExtendProperty());
        targetMetadata.setExtended(sourceMetadata.isExtended());
        targetMetadata.setPreviousVersion(sourceMetadata.getPreviousVersion());
        targetMetadata.setVersion(sourceMetadata.getVersion());
        targetMetadata.setProperties(sourceMetadata.getProperties());
        targetMetadata.setRefs(sourceMetadata.getRefs());
        return targetMetadata;
    }
}